﻿using System;
using System.Threading;
using System.Windows.Forms;
using MP3ToAudioBookWithChapters.Library;

namespace MP3ToAudioBookWithChapters.WinForms
{
    [System.ComponentModel.DesignerCategory("")]
    public class BackgroundThread : UserControl
    {
        Thread thread;

        public delegate void DoWorkDelegate();
        public delegate void WorkEndedDelegate();
        public delegate void WorkCancelledDelegate();
        public delegate void WorkProgressUpdatedDelegate(int percentComplete);
        public delegate void WorkLogUpdatedDelegate(string message, params string[] args);

        private DoWorkDelegate DoWork;
        public event WorkEndedDelegate WorkEnded;
        public event WorkProgressUpdatedDelegate WorkProgressUpdated;
        public event WorkLogUpdatedDelegate WorkLogUpdated;
        public event WorkCancelledDelegate WorkCancelled;

        public BackgroundThread()
        {
        }

        public void Initialize(DoWorkDelegate workDelegate)
        {
            if (thread != null)
                throw new MP3ToAudioBookWithChaptersException("Cannot initialize when thread is working");

            DoWork = workDelegate;
            WorkEnded = null;
            WorkProgressUpdated = null;
            WorkLogUpdated = null;
            WorkCancelled = null;

        }

        public void Begin()
        {
            if (thread != null)
                throw new MP3ToAudioBookWithChaptersException("Cannot begin when thread is working");

            thread = new Thread(new ThreadStart(DoWorkInternal));
            thread.Start();
        }

        public void UpdateProgress(int percentComplete)
        {
            if (WorkProgressUpdated != null)
            {
                if (this.InvokeRequired)
                {
                    this.Invoke(WorkProgressUpdated, percentComplete);
                }
                else
                {
                    WorkProgressUpdated(percentComplete);
                }
            }
        }

        public void UpdateLog(string message, params string[] args)
        {
            if (WorkLogUpdated != null)
            {
                if (this.InvokeRequired)
                {
                    this.Invoke(WorkLogUpdated, message, args);
                }
                else
                {
                    WorkLogUpdated(message, args);
                }
            }
        }

        private void DoWorkInternal()
        {
            DoWork();
            End();
        }

        private void End()
        {
            thread = null;
            NotifyWorkEnded();
        }

        public void Cancel()
        {
            if ((thread != null) &&
            (thread.ThreadState != ThreadState.Stopped && thread.ThreadState != ThreadState.Unstarted))
            {
                thread.Abort();
                var start = DateTime.UtcNow;
                while ((DateTime.UtcNow - start).TotalSeconds < 5)
                {
                    if (!thread.IsAlive)
                        break;
                    Thread.Sleep(100);
                }

                if (thread.IsAlive)
                    throw new MP3ToAudioBookWithChaptersException("Unable to quit worker thread");
            }

            NotifyWorkCancelled();

            thread = null;
        }

        private void NotifyWorkCancelled()
        {
            if (WorkCancelled != null)
            {
                if (this.InvokeRequired)
                {
                    this.Invoke(WorkCancelled);
                }
                else
                {
                    WorkCancelled();
                }
            }

        }

        private void NotifyWorkEnded()
        {
            if (WorkEnded != null)
            {
                if (this.InvokeRequired)
                {
                    this.Invoke(WorkEnded);
                }
                else
                {
                    WorkEnded();
                }
            }
        }
    }
}
